CanvasRenderingContext2D.prototype.drawRoundRect = function(sx,sy,ex,ey,r){
    var r2d = Math.PI/180;
    ex = ex + sx;
    ey = ey + sy;
    if( ( ex - sx ) - ( 2 * r ) < 0 ) { r = ( ( ex - sx ) / 2 ); } //ensure that the radius isn't too large for x
    if( ( ey - sy ) - ( 2 * r ) < 0 ) { r = ( ( ey - sy ) / 2 ); } //ensure that the radius isn't too large for y
    this.beginPath();
    this.moveTo(sx+r,sy);
    this.lineTo(ex-r,sy);
    this.arc(ex-r,sy+r,r,r2d*270,r2d*360,false);
    this.lineTo(ex,ey-r);
    this.arc(ex-r,ey-r,r,r2d*0,r2d*90,false);
    this.lineTo(sx+r,ey);
    this.arc(sx+r,ey-r,r,r2d*90,r2d*180,false);
    this.lineTo(sx,sy+r);
    this.arc(sx+r,sy+r,r,r2d*180,r2d*270,false);
    this.closePath();
}
CanvasRenderingContext2D.prototype.setShadow = function(ox,oy,blur,color){
    this.shadowOffsetX = ox    || 0;
    this.shadowOffsetY = oy    || 0;
    this.shadowBlur    = blur  || 0;
    this.shadowColor   = color || "black";
}
CanvasRenderingContext2D.prototype.removeShadow = function(){
    this.setShadow();
}
function isInRect(point,rect){
	return point.x >= rect.left && point.y >= rect.top && point.x <= (rect.left+rect.width) && point.y <= (rect.top+rect.height);
}
function Color(str){
    if(str.length == 7){
        str = str.substring(1,7);
    }
    var red = str.substring(0,2);
    var green = str.substring(2,4);
    var blue = str.substring(4,6);
    this.red = parseInt(red,16);
    this.green = parseInt(green,16);
    this.blue = parseInt(blue,16);
    this.alpha = 1;
    this.toString = function(){
        return "#"+zeroPad(this.red.toString(16),2)+zeroPad(this.green.toString(16),2)+zeroPad(this.blue.toString(16),2);
    }
    this.toRGBA = function(){
        return "rgba("+this.red+","+this.green+","+this.blue+","+this.alpha+")";
    }
}
function cutHex(h) {
	if(!h){
		return "000000";
	}
	return h.replace("#","");
}
function HexToR(h) {return parseInt(h.substring(0,2),16);}
function HexToG(h) {return parseInt(h.substring(2,4),16);}
function HexToB(h) {return parseInt(h.substring(4,6),16);}
function HexToRGB(h){
	h = cutHex(h);
	while(h.length < 6){
		h = "0"+h;
	}
	return HexToR(h)+","+HexToG(h)+","+HexToB(h);
}

String.prototype.parseInt = function(radix){
	return parseInt(this,radix);
};

function getSecondsFromDateString(string){
	// This function takes YouTube timecode strings (hh:mm:ss)
	split = string.split(":");
	return new Date(1970,0,0,18+Number(split[0]),Number(split[1]),Number(split[2])).getTime()/1000;
}
function parseAnnotations(XML){
	var annotations = {};
	var annotations_element = XML.getElementsByTagName("annotations")[0];
	var annotation_elements = annotations_element.getElementsByTagName("annotation");
	for(var i = 0; i < annotation_elements.length; i++){
		var annotation = annotation_elements[i];
		var newAnnotation = {};
		newAnnotation.eventHandlers = {};
		
		// BEGIN: Misc. block
			newAnnotation.id = annotation.getAttribute("id");
			newAnnotation.type = annotation.getAttribute("type");
			newAnnotation.style = annotation.getAttribute("style");
		// END: Misc. block
		
		// BEGIN: Text block
			if(annotation.getElementsByTagName("TEXT").length != 0){
				var textNode = annotation.getElementsByTagName("TEXT")[0];
				if(textNode.firstChild){
					newAnnotation.text = annotation.getElementsByTagName("TEXT")[0].firstChild.nodeValue;
				}else{
					newAnnotation.text = "";
				}
			}else{
				newAnnotation.text = "";
			}
		// END: Text block
		
		// BEGIN: Time/Position block
			var segment = annotation.getElementsByTagName("segment")[0].getElementsByTagName("movingRegion")[0];
			if(annotation.getElementsByTagName("segment")[0].hasAttribute("spaceRelative")){
				newAnnotation.spaceRelative = annotation.getElementsByTagName("segment")[0].getAttribute("spaceRelative");
			}else{
				newAnnotation.spaceRelative = false;
			}
			// BEGIN: Starting rectangle block
				var startSegment = segment.getElementsByTagName("rectRegion")[0];
				if(!startSegment){
					startSegment = segment.getElementsByTagName("anchoredRegion")[0];
				}
				var startRect = {};
				startRect.left = startSegment.getAttribute("x");
				startRect.top = startSegment.getAttribute("y");
				startRect.width = startSegment.getAttribute("w");
				startRect.height = startSegment.getAttribute("h");
				startRect.anchorx = startSegment.getAttribute("sx");
				startRect.anchory = startSegment.getAttribute("sy");
				if(startSegment.getAttribute("t") == "never"){
					annotation.display = false;
					startRect.time = false;
				}else{
					annotation.display = true;
					startRect.time = getSecondsFromDateString(startSegment.getAttribute("t"));
				}
				newAnnotation.startRect = startRect;
			// END: Starting rectangle block
				
			// START: Ending rectangle block
				var endSegment = segment.getElementsByTagName("rectRegion")[1];
				if(!endSegment){
					endSegment = segment.getElementsByTagName("anchoredRegion")[1];
				}
				if(endSegment){
					var endRect = {};
					endRect.left = endSegment.getAttribute("x");
					endRect.top = Math.abs(endSegment.getAttribute("y").parseInt());
					endRect.width = endSegment.getAttribute("w");
					endRect.height = endSegment.getAttribute("h");
					endRect.anchorx = endSegment.getAttribute("sx");
					endRect.anchory = endSegment.getAttribute("sy");
					if(endSegment.getAttribute("t") != "never"){
						endRect.time = getSecondsFromDateString(endSegment.getAttribute("t"));
					}else{
						endRect.time = false;
					}
					newAnnotation.endRect = endRect;
				}else{
					newAnnotation.endRect = startRect;
				}
			// END: Ending rectangle block
		// END: Time/Position block
		
		// BEGIN: Event handlers block (Actions)
			var actions = annotation.getElementsByTagName("action");
			for(var j = 0; j < actions.length; j++){
				var action = actions[j];
				var newEventHandler = {};
				if(action.getAttribute("type") == "openUrl"){
					newEventHandler.action = "openURL";
					newEventHandler.url = action.getElementsByTagName("url")[0].getAttribute("value");
					newAnnotation.isLink = true;
					newAnnotation.url = action.getElementsByTagName("url")[0].getAttribute("value");
				}else if(action.getAttribute("type") == "pause"){
					newEventHandler.action = "pause";
					newEventHandler.duration = getSecondsFromDateString(action.getElementsByTagName("duration")[0].getAttribute("value"));
					newAnnotation.endRect.time += 0.2;
				}
				if(action.getAttribute("trigger")){
					if(!newAnnotation.eventHandlers[action.getAttribute("trigger")]){
						newAnnotation.eventHandlers[action.getAttribute("trigger")] = [];
					}
					newAnnotation.eventHandlers[action.getAttribute("trigger")].push(newEventHandler);
				}else{
					if(!newAnnotation.eventHandlers.view){
						newAnnotation.eventHandlers.view = [];
					}
					newAnnotation.eventHandlers.view.push(newEventHandler);
				}
			}
		// END: Event handlers block
		
		// BEGIN: Event responders block (Trigger)
			newAnnotation.trigger = false;
			var triggers = annotation.getElementsByTagName("trigger");
			var trigger = triggers[0];
			if(trigger){
				var condition = trigger.getElementsByTagName("condition")[0];
				newAnnotation.trigger = {
					ref:   condition.getAttribute("ref"),
					state: condition.getAttribute("state")
				};
			}
		// END: Event responders block
		
		// BEGIN: Appearance block
			if(annotation.getElementsByTagName("appearance").length > 0){
				var appearance = annotation.getElementsByTagName("appearance")[0];
				newAnnotation.fgColor =				Number(appearance.getAttribute("fgColor")).toString(16);
				newAnnotation.bgColor =				Number(appearance.getAttribute("bgColor")).toString(16);
				newAnnotation.borderColor =			Number(appearance.getAttribute("borderColor")).toString(16);
				newAnnotation.borderWidth =			Number(appearance.getAttribute("borderWidth")).toString(10);
				newAnnotation.bgAlpha =				appearance.getAttribute("bgAlpha");
				newAnnotation.borderAlpha =			appearance.getAttribute("borderAlpha");
				newAnnotation.gloss =				Number(appearance.getAttribute("gloss"));
				newAnnotation.highlightFontColor =	Number(appearance.getAttribute("highlightFontColor")).toString(16);
				newAnnotation.highlightWidth =		Number(appearance.getAttribute("highlightWidth")).toString(10);
				newAnnotation.italic = 				appearance.getAttribute("fontStyle") == "italic";
				newAnnotation.bold = 				appearance.getAttribute("fontWeight") == "bold";
				var loc3 = parseFloat(appearance.getAttribute("textSize"));
				if(!isNaN(loc3) && loc3 > 0){
					newAnnotation.textSize = loc3;
				}else{
					newAnnotation.textSize = 0.036107;
				}
			}else{
				newAnnotation.highlightFontColor = "f2f2f2";
				newAnnotation.bold = false;
				newAnnotation.gloss = 0;
				newAnnotation.textSize = 0.036107;
				newAnnotation.borderAlpha = 0.2;
				newAnnotation.borderColor = "000000";
				newAnnotation.bgAlpha = "0.8";
				newAnnotation.bgColor = "ffffff";
				newAnnotation.fgColor = "1a1a1a";
				newAnnotation.italic = false;
				newAnnotation.highlightWidth = 3;
				newAnnotation.borderWidth = 0;
			}
		// END: Appearance block
		annotations[newAnnotation.id] = newAnnotation;
	}
	return annotations;
}

function addAnnotationsEl(videoEl){
	var contentDiv = videoEl.parentNode;
	var canvas = document.createElement("canvas");
	canvas.width = 0;
	canvas.height = 0;
	// Set convenience properties
	canvas.videoEl = videoEl;
	canvas.contentDiv = contentDiv;
	videoEl.canvas = canvas;
	contentDiv.canvas = canvas;
	canvas.classList.add("html5-video-canvas");
	canvas.addEventListener("mousemove",function(event){
		canvas.mousePos = {x: event.offsetX, y: event.offsetY};
		updateAnnotations.call(videoEl);
	},false);
	canvas.addEventListener("mouseout",function(event){
		canvas.mousePos = {x: -1, y: -1};
		updateAnnotations.call(videoEl);
	},false);
	canvas.addEventListener("click",function(event){
		if(this.link){
			this.videoEl.pause();
			window.open(this.link);
		}
	},false);
	// Put the canvas in the content element
	contentDiv.appendChild(canvas);
}

function renderAnnotation(annotation,ctx,canvas,pos,mousedOver){
	if      (annotation.type  == "highlight"){
		renderHighlight(annotation,ctx,pos,mousedOver);
	}else if(annotation.style == "highlightText"){
		renderHighlightText(annotation,ctx,pos,mousedOver);
	}
}

function renderHighlight(annotation,ctx,pos,mousedOver){
	ctx.lineWidth = annotation.highlightWidth;
	var color = new Color(annotation.bgColor);
	color.alpha = mousedOver?0.8:0.25;
	ctx.strokeStyle = color.toRGBA();
	ctx.drawRoundRect(pos.left,pos.top,pos.width,pos.height,5);
	ctx.stroke();
	if(mousedOver && annotation.isLink){
		ctx.canvas.link = annotation.url;
	}
}

function renderHighlightText(annotation,ctx,pos,mousedOver){
	var color = new Color("ffffff");
	//ctx.setShadow(0,0,6,"rgba(0,0,0,0.5)");
	ctx.font = "Arial Sans";
	ctx.textAlign = "center";
	ctx.fillStyle = color.toRGBA();
	ctx.fillText(annotation.text,pos.left,pos.top,pos.width);
	ctx.removeShadow();
}

function updateAnnotations(){
	var annotations = this.annotations;
	var contentDiv = this.parentNode;
	var currentTime = this.currentTime;
	var playerId = this.playerId;
	var canvas = this.canvas;
	var ctx = canvas.getContext("2d");
	canvas.link = false;
	canvas.width = this.scrollWidth;
	canvas.height = this.scrollHeight;
	var mousePos = canvas.mousePos || {x: -1, y: -1};
	var mouseTriggers = {
		click: [],
		over: [],
		out: []
	};
	canvas.showHighlightTexts = [];
	for(var i in annotations){
		var annotation = annotations[i];
		
		var pos = {
			top:    annotation.startRect.top,
			left:   annotation.startRect.left,
			width:  annotation.startRect.width,
			height: annotation.startRect.height,
		};
		
		if(annotation.spaceRelative){
			var relativeAnn = annotations[annotation.spaceRelative];
			pos.top    = pos.top + relativeAnn.startRect.top;
			pos.left   = pos.left + relativeAnn.startRect.left;
			pos.width  = Math.min(pos.width,  relativeAnn.startRect.width);
			pos.height = Math.min(pos.height, relativeAnn.startRect.height);
		}
		
		pos.top    = (pos.top   /100)*canvas.height;
		pos.left   = (pos.left  /100)*canvas.width;
		pos.width  = (pos.width /100)*canvas.width;
		pos.height = (pos.height/100)*canvas.height;
		
		if(!(annotation.startRect.time === false)){
			if(!(currentTime >= annotation.startRect.time && currentTime <= annotation.endRect.time)){
				continue;
			}
		}else if(annotation.trigger){
			var trigger = annotations[annotation.trigger.ref];
			var triggerPos = {
				left:   (trigger.startRect.left  /100)*canvas.width,
				top:    (trigger.startRect.top   /100)*canvas.height,
				width:  (trigger.startRect.width /100)*canvas.width,
				height: (trigger.startRect.height/100)*canvas.height,
			}
			if(!isInRect(mousePos,triggerPos) || !(currentTime >= trigger.startRect.time && currentTime <= trigger.endRect.time)){
				continue;
			}
			console.log(pos);
		}else{
			continue;
		}
		
		if(annotation.type == "pause"){
			  //doPause(this,annotation.eventHandlers.view[0].duration);
			  continue;
		}
		
		var mousedOver = false;
		if(isInRect(mousePos,pos)){
			mousedOver = true;
		}
		
		renderAnnotation(annotation,ctx,canvas,pos,mousedOver);
		
		/*
		currentAnnotation = document.createElement("div");
		currentAnnotation.className = "video-annotation video-annotation-" + annotation.type + " video-annotation-" + annotation.style;
		currentAnnotation.id = "video-annotation-" + playerId + "-" + annotation.id;
		currentAnnotation.style.left = annotation.startRect.left + "%";
		currentAnnotation.style.top = annotation.startRect.top + "%";
		currentAnnotation.style.width = annotation.startRect.width + "%";
		currentAnnotation.style.height = annotation.startRect.height + "%";
		currentAnnotation.innerHTML = annotation.text;
		
		if(annotation.startRect.time === false){
			currentAnnotation.style.display = "none";
		}
		*/
		
		/*
		
		if(annotation.eventHandlers.click){
			for(var j = 0; j < annotation.eventHandlers.click.length; j++){
				currentAnnotation.style.setProperty("cursor","pointer");
				var eventHandler;
				if(annotation.eventHandlers.click[j].action == "openURL"){
					annotation.isLink = true;
					var url = annotation.eventHandlers.click[j].url;
					currentAnnotation.url = annotation.eventHandlers.click[j].url;
					eventHandler = function(){
						window.location.href = this.url;
					};
					currentAnnotation.className = currentAnnotation.className + " video-annotation-link";
					if(url.indexOf("/watch") != -1){
						annotation.linkType = "video";
						currentAnnotation.className = currentAnnotation.className + " video-annotation-link-video";
					}else if(url.indexOf("/view_play_list") != -1){
						annotation.linkType = "playlist";
						currentAnnotation.className = currentAnnotation.className + " video-annotation-link-playlist";
					}else if(url.indexOf("/profile") != -1){
						annotation.linkType = "channel";
						currentAnnotation.className = currentAnnotation.className + " video-annotation-link-channel";
					}else if(url.indexOf("/subscription_center") != -1){
						annotation.linkType = "subscribe";
						currentAnnotation.className = currentAnnotation.className + " video-annotation-link-subscribe";
					}else if(url.indexOf("/inbox?action_compose") != -1){
						annotation.linkType = "compose";
						currentAnnotation.className = currentAnnotation.className + " video-annotation-link-compose";
					}else if(url.indexOf("/group") != -1){
						annotation.linkType = "group";
						currentAnnotation.className = currentAnnotation.className + " video-annotation-link-group";
					}else if(url.indexOf("&layer_token=") != -1){
						annotation.linkType = "annotate";
						currentAnnotation.className = currentAnnotation.className + " video-annotation-link-annotate";
					}else if(url.indexOf("/video_response_upload") != -1){
						annotation.linkType = "respond";
						currentAnnotation.className = currentAnnotation.className + " video-annotation-link-respond";
					}else if(url.indexOf("/results") != -1){
						annotation.linkType = "results";
						currentAnnotation.className = currentAnnotation.className + " video-annotation-link-results";
					}else{
						annotation.linkType = "other";
						currentAnnotation.className = currentAnnotation.className + " video-annotation-link-other";
					}
					currentAnnotation.title = url;
				}
				currentAnnotation.addEventListener("click",eventHandler,true);
			}
		}
		
		if(annotation.eventHandlers.trigger){
			for(var m = 0; m < annotation.eventHandlers.trigger.length; m++){
				var trigger = annotation.eventHandlers.trigger[m];
				currentAnnotation.triggerId = trigger.trigger;
				currentAnnotation.idBase = "video-annotation-" + playerId + "-";
				currentAnnotation.addEventListener("mouseover",function(){document.getElementById(this.idBase + this.triggerId).style.display = "block";},false);
				currentAnnotation.addEventListener("mouseout",function(){document.getElementById(this.idBase + this.triggerId).style.display = "none";},false);
			}
		}
		
		if(annotation.type == "text"){
			currentAnnotation.style.color = "#"+annotation.fgColor;
			if(annotation.gloss > 0){
				if(annotation.isLink){
					currentAnnotation.style.setProperty("background-image","url("+safari.extension.baseURI+"Images/links/"+annotation.linkType+"Link.png), -webkit-gradient(linear, left top, left bottom, color-stop(0, rgb(255,255,255)), color-stop("+(annotation.gloss/100)+", rgba("+HexToRGB(annotation.bgColor)+","+annotation.bgAlpha+")))");
					currentAnnotation.style.setProperty("background-position", "bottom right, top left");
					currentAnnotation.style.setProperty("background-repeat","no-repeat");
					currentAnnotation.addEventListener("mouseover",function(){this.style.backgroundImage = this.style.backgroundImage.replace("Link.png","Link-over.png");},false);
					currentAnnotation.addEventListener("mouseout",function(){this.style.backgroundImage = this.style.backgroundImage.replace("Link-over.png","Link.png");},false);
				}else{
					currentAnnotation.style.setProperty("background-image","-webkit-gradient(linear, left top, left bottom, color-stop(0, rgb(255,255,255)), color-stop("+(annotation.gloss/100)+", rgba("+HexToRGB(annotation.bgColor)+","+annotation.bgAlpha+")))");
				}
			}else{
				currentAnnotation.overBorderColor = HexToRGB(annotation.highlightFontColor);
				currentAnnotation.normalBorderColor = HexToRGB(annotation.borderColor);
				currentAnnotation.normalBorderWidth = annotation.borderWidth;
				currentAnnotation.normalBorderAlpha = annotation.borderAlpha;
				currentAnnotation.linkType = annotation.linkType;
				if(annotation.isLink){
					currentAnnotation.style.setProperty("background-image","url("+safari.extension.baseURI+"Images/links/"+annotation.linkType+"Link.png)");
					currentAnnotation.style.setProperty("background-position", "bottom right");
					currentAnnotation.style.setProperty("background-repeat","no-repeat");
					currentAnnotation.addEventListener("mouseover",function(){this.style.backgroundImage = this.style.backgroundImage.replace("Link.png","Link-over.png");},false);
					currentAnnotation.addEventListener("mouseout",function(){this.style.backgroundImage = this.style.backgroundImage.replace("Link-over.png","Link.png");},false);
				}
				currentAnnotation.style.setProperty("background-color","rgba("+HexToRGB(annotation.bgColor)+","+annotation.bgAlpha+")");
				currentAnnotation.addEventListener("mouseover",function(){this.style.border = "1px solid rgba("+this.overBorderColor+",1)";});
				currentAnnotation.addEventListener("mouseout", function(){this.style.border = this.normalBorderWidth+"px solid rgba("+this.normalBorderColor+","+this.normalBorderAlpha+")";});
			}
			currentAnnotation.style.borderColor = "rgba("+HexToRGB(annotation.borderColor)+","+annotation.borderAlpha+")";
			currentAnnotation.style.borderWidth = annotation.borderWidth + "px";
		}else if(annotation.type == "highlight"){
			if(annotation.isLink){
				currentAnnotation.style.setProperty("background-image","url("+safari.extension.baseURI+"Images/links/"+annotation.linkType+"Link.png)");
				currentAnnotation.style.setProperty("background-position", "bottom right");
				currentAnnotation.style.setProperty("background-repeat","no-repeat");
				currentAnnotation.addEventListener("mouseover",function(){this.style.backgroundImage = this.style.backgroundImage.replace("Link.png","Link-over.png");},false);
				currentAnnotation.addEventListener("mouseout",function(){this.style.backgroundImage = this.style.backgroundImage.replace("Link-over.png","Link.png");},false);
			}
			currentAnnotation.rgbColor = HexToRGB(annotation.bgColor);
			currentAnnotation.bgAlpha = annotation.bgAlpha;
			currentAnnotation.borderWidth = annotation.highlightWidth;
			currentAnnotation.style.border = annotation.highlightWidth+"px solid rgba("+HexToRGB(annotation.bgColor)+","+annotation.bgAlpha+")";
			currentAnnotation.addEventListener("mouseover",function(){this.style.border = this.borderWidth+"px solid rgba("+this.rgbColor+",1)";},false);
			currentAnnotation.addEventListener("mouseout",function(){this.style.border = this.borderWidth+"px solid rgba("+this.rgbColor+","+this.bgAlpha+")";},false);
		}
		contentDiv.appendChild(currentAnnotation);
		if(annotation.style == "speech"){
			currentAnnotation.style.paddingLeft = currentAnnotation.style.paddingRight =  Math.round(0.01248 * currentAnnotation.clientHeight * 4 / 3) + "px";
			currentAnnotation.style.paddingTop  = currentAnnotation.style.paddingBottom = Math.round(0.01248 * currentAnnotation.clientHeight)         + "px";
		}
		*/
	}
	if(canvas.link){
		canvas.style.cursor = "pointer";
	}else{
		canvas.style.cursor = "initial";
	}
}

function updatePauseEl(time,el){
	if(time > 0){
		el.innerHTML = "Video paused for "+time+" seconds.";
		video.updatePauseAnnTimeout = setTimeout(updatePauseEl,1000,time-1,el);
	}else{
		el.parentNode.removeChild(el);
	}
}

function doPause(video,duration){
	if(!video.lockedPause){
		video.pause();
		var pauseEl = document.createElement("div");
		pauseEl.className = "video-annotation video-annotation-text video-annotation-popup video-annotation-pause";
		pauseEl.style.color = "black";
		pauseEl.style.backgroundColor = "white";
		pauseEl.style.borderColor = "black";
		pauseEl.style.left = "0%";
		pauseEl.style.top = "0%";
		pauseEl.style.width = "100%";
		pauseEl.style.textAlign = "center";
		pauseEl.innerHTML = "Video paused for "+duration+" seconds.";
		video.updatePauseAnnTimeout = setTimeout(updatePauseEl,1000,duration-1,pauseEl);
		video.parentNode.appendChild(pauseEl);
		video.playTimeout = setTimeout(function(video){video.play();video.lockedPause = true;setTimeout(function(video){video.lockedPause = false;},100,video);},duration*1000,video);
	}
}